/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.appspot.trafficando.domain.traffic;

import com.appspot.trafficando.domain.EventCategory;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 *
 * @author Domenico Maria Giffone
 */
public final class TrafficEventCategory implements Serializable, Comparable<TrafficEventCategory>, EventCategory {

    private static final long serialVersionUID = 258308464356906721L;
    private static final ConcurrentMap<String, List<TrafficEventCategory>> categoriesByScheme = new ConcurrentHashMap<String, List<TrafficEventCategory>>();
    private static final ConcurrentMap<String, TrafficEventCategory> categoriesByTerm = new ConcurrentHashMap<String, TrafficEventCategory>();
    private String scheme;
    private String label;
    private String term;

    static {
        try {
            String clsName = System.getProperty(
                    "com.appspot.trafficando.domain.traffic.TrafficEventCategoryDataProvider", "com.appspot.trafficando.domain.traffic.TrafficEventCategoryDataProvider$DefaultProvider");
            Class<? extends TrafficEventCategoryDataProvider> cls =
                    TrafficEventCategory.class.getClassLoader().loadClass(clsName).asSubclass(TrafficEventCategoryDataProvider.class);
            cls.newInstance().registerTypes();
        } catch (Exception ex) {
            throw new RuntimeException(ex.toString(), ex);
        }
    }

    private TrafficEventCategory(String term, String scheme, String label) {
        super();
        this.term = term;
        this.scheme = scheme;
        this.label = label;
    }

    static void registerType(String term, String scheme, String label) {
        TrafficEventCategory cat = new TrafficEventCategory(term, scheme, label);
        if (categoriesByTerm.putIfAbsent(term, cat) != null) {
            throw new IllegalArgumentException("Category already registered with this term: " + term);
        }
        if (categoriesByScheme.containsKey(scheme) == false) {
            List<TrafficEventCategory> categories = new CopyOnWriteArrayList<TrafficEventCategory>();
            categoriesByScheme.put(scheme, categories);
        }
        List<TrafficEventCategory> categories = categoriesByScheme.get(scheme);
        categories.add(cat);
    }

    public static TrafficEventCategory first() {
        final Iterator<TrafficEventCategory> iterator = categoriesByTerm.values().iterator();
        if (iterator.hasNext()) {
            return iterator.next();
        }
        throw new RuntimeException("No TrafficEventCategory registered");
    }

    @Override
    public List<? extends EventCategory> registeredCategories() {
        List<TrafficEventCategory> list = new ArrayList<TrafficEventCategory>(categoriesByTerm.values());
        Collections.sort(list);
        return list;
    }

    @Override
    public List<? extends EventCategory> within(String scheme) {
        return (categoriesByScheme.containsKey(scheme) == true) ? categoriesByScheme.get(scheme) : new CopyOnWriteArrayList<TrafficEventCategory>();
    }

    @Override
    @SuppressWarnings("unchecked")
    public <T extends EventCategory> T of(String term) {
        return (T) categoriesByTerm.get(term);
    }

    @Override
    public int compareTo(TrafficEventCategory o) {
        return term.compareTo(o.term);
    }

    @Override
    public String getScheme() {
        return scheme;
    }

    @Override
    public String getTerm() {
        return term;
    }

    @Override
    public String getLabel() {
        return label;
    }
}
